Introduction

John Tukey is often regarded as one of the founding drivers for modern data science, incorporating statistical inference along with basic summary statistics to visualize and understand data.

# Load libraries
suppressPackageStartupMessages(library(tidyverse))
library(curl)
library(ggtext)
library(scales)
library(patchwork)
library(kableExtra)
library(matrixStats)

# Set ggplot2 theme
my_theme <- theme(
  panel.grid = element_blank(), 
  panel.background = element_rect(fill = "white", color = "black"),
  
  # Set axis and title themes
  plot.title = element_markdown()
)

Elements of Structured Data

The two main types of structured data (i.e., data in a table similar to an Excel spreadsheet or something similar) are numeric and categorical:

  • Numeric:

    • Continuous: any real number (e.g., velocity, flow rates)

    • Discrete: counts (e.g., number of children per household)

  • Categorical: fixed set of values (e.g., car models, species of fish)

    • Binary: true/false, 0/1, yes/no values

    • Ordinal: fixed set of values with specified order to denote rating (e.g., 1-5)

Estimates of Location

The mean describes the average value, taken by summing all of the values and dividing by the total number of records (i.e., total number of observations).

# Take the mean
mean(random_data)
[1] 9.940272

If some cases, the mean may not be an appropriate summary statistic for the dataset. For example, the presence of outliers could skew the mean and lead to eroneous conclusions. In these cases, it may be appropriate to use the trimmed mean, which is calculated by dropping a fixed set of of sorted values from each end of the distribution. The weighted mean is another variation that may be used in situations where data from some features are more important than others, due to intrinsic variability in some features, or due to sampling design that wasn’t representative of the entire population.

We can calculate the trimmed mean using the mean() function and declaring the trim argument to remove a fraction of observations from each end of the distribution before the mean is calculated.

mean(random_data, trim = 0.5)
[1] 9.904327

We can calculate the weighted mean using the weighted.mean() function, which takes two arguments: x to define the object containing the values whose weighted mean we want to calculate; and w, to define an object of the same length of x giving the weights of each element of x. In the example below, we’ll use data from Siegel 1994 (also available in the help documentation) to calculate the weighted GPA, which is the GPA that considers the grade the student received in a course as well as the difficulty of the course.

# GPA from Siegel 1994- example from the help documentation
wt <- c(5, 5, 4, 1)/15
x <- c(3.7, 3.3, 3.5, 2.8)

# Calculate the weighted mean pf GPA for each quarter, weighted by the course load
weighted.mean(x, wt)
[1] 3.453333

Median and Robust Estimates

The median is less sensitive to the data, meaning that it is robust to outliers present in a dataset. In cases with an odd number of records, the median is the middle number; in cases with an even number of records, the median is the average between the two middle numbers that divide the data into lower and upper halves.

Anomaly Detection

Although sometimes, outliers (extreme data points far from the mean) are mistakes made from data entry, there would be something interesting going on to warrant further investigation. In general, though, either the median or trimmed mean are used, trimming 10% of the lowest and highest values from the sorted distribution before calculating the mean, in the latter case.

Example: Location Estimates of Population and Murder Rates

In this example, we’ll explore the murder rates across the country in the states.csv file.

We can compute various summary statistics on this dataset to get an overview of murder rates across the United States.

Population Size
Mean Mean (trimmed) Median
6,162,876 4,783,697 4,436,370

We can also calculate the weighted mean and median, which will take into account the population size for each state. The key takeaway here is that both estimates are relatively similar.

Weighted statistics
Mean Median
4.445834 4.4

Estimates of Variability

Variability describes the spread of data and distinguishing it from real variability. There are a few terms to review, including deviations (differences between the observed and estimated values, like the mean, for example), variance (sum of squared deviations from the mean, divided by the sample size minus one), and the standard deviation (the square root of the variance). There are many other metrics used to describe variability, which will be demonstrated using examples below.

One metric to describe variability is the mean absolute deviation, or the average of the absolute differences between the observed and estimated values. More commonly used metrics are the variance (average of the squared deviations) and the standard deviation. The latter metric is preferred over the variance because it is in the same scale as the original data.

Often, the variance and standard deviation formulas use n-1 in their denominators to generate unbiased estimates. When using just n, the sample estimates of the variance and standard deviations will be biased and underestimate the true population parameters, because of the constraint that the standard deviation requires calculating the sample mean.

Variance and standard deviation are both sensitive to outliers, but another metric, the median absolute deviation from the median (MAD), is not (similar to the median).

Estimates Based on Percentiles

The percentile is the value at which a given percentage of the data are a particular value, or less. For example, if we took a sequence of numbers from 1 to 1,000 and piped them into the quantile() function, we’d see that the 25th percentile is 250.75. In other words, 25% of the values from 1 to 1,000 are 250.75 or less. The IQR() function will yield the interquartile range, or the essentially where the middle 50% of the data lie.

Below, we’ll explore the states dataset using the estimates of variability.

From this plot, it’s obvious that the standard deviation (SD) is about 1.7 times greater than the MAD, because it is sensitive to outliers.

Exploring the Data Distribution

There are several additional approaches to visualizing distributions, including histograms and density plots. These two visualizations are similar, except the histograms are based on binning observations into discrete bins, while the latter involves kernel density estimation (KDE) to approximate the probability distribution. KDE is a non-parametric approach to approximating the probability distribution because sometimes data don’t conform to the standard probability distributions (e.g., if data are bimodal, or multimodal). In parametric approaches, we can describe the probability distribution using parameters. For example, if data are normally distributed, we describe the probability distribution using two parameters: the mean and standard deviation.

To describe the middle 50% of the data for various fields, or the range between the 25th and 75th percentile, we can use a boxplot. The data points outside of 1.5 times the IQR are defined as outliers (also shown). Note in the boxplots, the function call should be ggplot(aes("", Population)), or else there will be erroneous data on the x-axis.

Statistical Moments

There are four moments of a distribution:

  1. Location (referring to mean, median, mode)

  2. Variability (e.g., standard deviation, variance, MAD)

  3. Skewness (are data skewed to large or small values)

  4. Kurtosis (describes the tendency for data to have extreme values)

Exploring Binary and Categorical Data

In the next section, we’ll explore categorical data using the dfw_airline.csv dataset, which contains data on the number of delayed flights per year from Dallas/Fort Worth airport due to various reasons: carrier issues, air traffic control problems, weather, security, or issues with inbound flights.

Carrier ATC Weather Security Inbound
64263.16 84856.5 11235.42 343.15 118427.8

These data can also be represented using a bar plot.

Correlation

The correlation describes the relationship between two variables on a continuous -1 to 1 scale. It is calculated by taking the deviations from the mean for one variable, multiplied by the deviations from the mean for the second variable, and then dividing by the product of the standard deviations. In this section, we’ll explore the sp500_px.csv dataset, which contains 5,647 records of telecommunication stocks from 517 different companies over time. To create the plot in the textbook, we’ll also import the sp500_sym.csv dataset, which contains data on the company symbols and sectors. We’ll join both of these together, then filter the final dataset to only include those records for major exchange-tranded funds (ETFs) between July 2012 through June 2015.

Now, we’ll calculate the correlation of daily returns between all companies.

etf_data %>% 
  select(symbol, daily_returns) %>% 
  mutate(rownumber = row_number()) %>% 
  pivot_wider(id_cols = rownumber, 
              names_from = "symbol", 
              values_from = "daily_returns")
LS0tCnRpdGxlOiAiQ2hhcHRlciAxOiBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0aGVtZTogY2VydWxlYW4KICAgIHN5bnRheDogaGlnaGxpZ2h0Ci0tLQoKIyMgSW50cm9kdWN0aW9uCgpKb2huIFR1a2V5IGlzIG9mdGVuIHJlZ2FyZGVkIGFzIG9uZSBvZiB0aGUgZm91bmRpbmcgZHJpdmVycyBmb3IgbW9kZXJuIGRhdGEgc2NpZW5jZSwgaW5jb3Jwb3JhdGluZyBzdGF0aXN0aWNhbCBpbmZlcmVuY2UgYWxvbmcgd2l0aCBiYXNpYyBzdW1tYXJ5IHN0YXRpc3RpY3MgdG8gdmlzdWFsaXplIGFuZCB1bmRlcnN0YW5kIGRhdGEuCgpgYGB7ciBzZXR1cCwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyBMb2FkIGxpYnJhcmllcwpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSh0aWR5dmVyc2UpKQpsaWJyYXJ5KGN1cmwpCmxpYnJhcnkoZ2d0ZXh0KQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShtYXRyaXhTdGF0cykKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGNvcnJwbG90KQoKIyBTZXQgZ2dwbG90MiB0aGVtZQpteV90aGVtZSA8LSB0aGVtZSgKICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCAKICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJibGFjayIpLAogIAogICMgU2V0IGF4aXMgYW5kIHRpdGxlIHRoZW1lcwogIHBsb3QudGl0bGUgPSBlbGVtZW50X21hcmtkb3duKCksCiAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfbWFya2Rvd24oKQopCmBgYAoKCiMjIEVsZW1lbnRzIG9mIFN0cnVjdHVyZWQgRGF0YQoKVGhlIHR3byBtYWluIHR5cGVzIG9mIHN0cnVjdHVyZWQgZGF0YSAoaS5lLiwgZGF0YSBpbiBhIHRhYmxlIHNpbWlsYXIgdG8gYW4gRXhjZWwgc3ByZWFkc2hlZXQgb3Igc29tZXRoaW5nIHNpbWlsYXIpIGFyZSBudW1lcmljIGFuZCBjYXRlZ29yaWNhbDoKCiogTnVtZXJpYzoKCiAgKiBDb250aW51b3VzOiBhbnkgcmVhbCBudW1iZXIgKGUuZy4sIHZlbG9jaXR5LCBmbG93IHJhdGVzKQoKICAqIERpc2NyZXRlOiBjb3VudHMgKGUuZy4sIG51bWJlciBvZiBjaGlsZHJlbiBwZXIgaG91c2Vob2xkKQogIAoqIENhdGVnb3JpY2FsOiBmaXhlZCBzZXQgb2YgdmFsdWVzIChlLmcuLCBjYXIgbW9kZWxzLCBzcGVjaWVzIG9mIGZpc2gpCgogICogQmluYXJ5OiB0cnVlL2ZhbHNlLCAwLzEsIHllcy9ubyB2YWx1ZXMKICAKICAqIE9yZGluYWw6IGZpeGVkIHNldCBvZiB2YWx1ZXMgd2l0aCBzcGVjaWZpZWQgb3JkZXIgdG8gZGVub3RlIHJhdGluZyAoZS5nLiwgMS01KQogIAojIyBFc3RpbWF0ZXMgb2YgTG9jYXRpb24KClRoZSBtZWFuIGRlc2NyaWJlcyB0aGUgYXZlcmFnZSB2YWx1ZSwgdGFrZW4gYnkgc3VtbWluZyBhbGwgb2YgdGhlIHZhbHVlcyBhbmQgZGl2aWRpbmcgYnkgdGhlIHRvdGFsIG51bWJlciBvZiByZWNvcmRzIChpLmUuLCB0b3RhbCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zKS4KCmBgYHtyIG1lYW59CiMgRGVmaW5lIGEgcmFuZG9tIGRhdGFzZXQKcmFuZG9tX2RhdGEgPC0gcm5vcm0oMTAwLCAxMCwgNSkKCiMgVGFrZSB0aGUgbWVhbgptZWFuKHJhbmRvbV9kYXRhKQpgYGAKCklmIHNvbWUgY2FzZXMsIHRoZSBtZWFuIG1heSBub3QgYmUgYW4gYXBwcm9wcmlhdGUgc3VtbWFyeSBzdGF0aXN0aWMgZm9yIHRoZSBkYXRhc2V0LiAgRm9yIGV4YW1wbGUsIHRoZSBwcmVzZW5jZSBvZiBvdXRsaWVycyBjb3VsZCBza2V3IHRoZSBtZWFuIGFuZCBsZWFkIHRvIGVyb25lb3VzIGNvbmNsdXNpb25zLiAgSW4gdGhlc2UgY2FzZXMsIGl0IG1heSBiZSBhcHByb3ByaWF0ZSB0byB1c2UgdGhlIHRyaW1tZWQgbWVhbiwgd2hpY2ggaXMgY2FsY3VsYXRlZCBieSBkcm9wcGluZyBhIGZpeGVkIHNldCBvZiBvZiBzb3J0ZWQgdmFsdWVzIGZyb20gZWFjaCBlbmQgb2YgdGhlIGRpc3RyaWJ1dGlvbi4gIFRoZSB3ZWlnaHRlZCBtZWFuIGlzIGFub3RoZXIgdmFyaWF0aW9uIHRoYXQgbWF5IGJlIHVzZWQgaW4gc2l0dWF0aW9ucyB3aGVyZSBkYXRhIGZyb20gc29tZSBmZWF0dXJlcyBhcmUgbW9yZSBpbXBvcnRhbnQgdGhhbiBvdGhlcnMsIGR1ZSB0byBpbnRyaW5zaWMgdmFyaWFiaWxpdHkgaW4gc29tZSBmZWF0dXJlcywgb3IgZHVlIHRvIHNhbXBsaW5nIGRlc2lnbiB0aGF0IHdhc24ndCByZXByZXNlbnRhdGl2ZSBvZiB0aGUgZW50aXJlIHBvcHVsYXRpb24uICAKCldlIGNhbiBjYWxjdWxhdGUgdGhlIHRyaW1tZWQgbWVhbiB1c2luZyB0aGUgYG1lYW4oKWAgZnVuY3Rpb24gYW5kIGRlY2xhcmluZyB0aGUgYHRyaW1gIGFyZ3VtZW50IHRvIHJlbW92ZSBhIGZyYWN0aW9uIG9mIG9ic2VydmF0aW9ucyBmcm9tIGVhY2ggZW5kIG9mIHRoZSBkaXN0cmlidXRpb24gYmVmb3JlIHRoZSBtZWFuIGlzIGNhbGN1bGF0ZWQuCgpgYGB7ciB0cmltbWVkLW1lYW59Cm1lYW4ocmFuZG9tX2RhdGEsIHRyaW0gPSAwLjUpCmBgYAoKV2UgY2FuIGNhbGN1bGF0ZSB0aGUgd2VpZ2h0ZWQgbWVhbiB1c2luZyB0aGUgYHdlaWdodGVkLm1lYW4oKWAgZnVuY3Rpb24sIHdoaWNoIHRha2VzIHR3byBhcmd1bWVudHM6IGB4YCB0byBkZWZpbmUgdGhlIG9iamVjdCBjb250YWluaW5nIHRoZSB2YWx1ZXMgd2hvc2Ugd2VpZ2h0ZWQgbWVhbiB3ZSB3YW50IHRvIGNhbGN1bGF0ZTsgYW5kIGB3YCwgdG8gZGVmaW5lIGFuIG9iamVjdCBvZiB0aGUgc2FtZSBsZW5ndGggb2YgYHhgIGdpdmluZyB0aGUgd2VpZ2h0cyBvZiBlYWNoIGVsZW1lbnQgb2YgYHhgLiAgSW4gdGhlIGV4YW1wbGUgYmVsb3csIHdlJ2xsIHVzZSBkYXRhIGZyb20gU2llZ2VsIDE5OTQgKGFsc28gYXZhaWxhYmxlIGluIHRoZSBoZWxwIGRvY3VtZW50YXRpb24pIHRvIGNhbGN1bGF0ZSB0aGUgd2VpZ2h0ZWQgR1BBLCB3aGljaCBpcyB0aGUgR1BBIHRoYXQgY29uc2lkZXJzIHRoZSBncmFkZSB0aGUgc3R1ZGVudCByZWNlaXZlZCBpbiBhIGNvdXJzZSBhcyB3ZWxsIGFzIHRoZSBkaWZmaWN1bHR5IG9mIHRoZSBjb3Vyc2UuCgpgYGB7ciB3ZWlnaHRlZC1tZWFufQojIEdQQSBmcm9tIFNpZWdlbCAxOTk0LSBleGFtcGxlIGZyb20gdGhlIGhlbHAgZG9jdW1lbnRhdGlvbgp3dCA8LSBjKDUsIDUsIDQsIDEpLzE1CnggPC0gYygzLjcsIDMuMywgMy41LCAyLjgpCgojIENhbGN1bGF0ZSB0aGUgd2VpZ2h0ZWQgbWVhbiBwZiBHUEEgZm9yIGVhY2ggcXVhcnRlciwgd2VpZ2h0ZWQgYnkgdGhlIGNvdXJzZSBsb2FkCndlaWdodGVkLm1lYW4oeCwgd3QpCmBgYAoKIyMgTWVkaWFuIGFuZCBSb2J1c3QgRXN0aW1hdGVzCgogVGhlIG1lZGlhbiBpcyBsZXNzIHNlbnNpdGl2ZSB0byB0aGUgZGF0YSwgbWVhbmluZyB0aGF0IGl0IGlzIHJvYnVzdCB0byBvdXRsaWVycyBwcmVzZW50IGluIGEgZGF0YXNldC4gIEluIGNhc2VzIHdpdGggYW4gb2RkIG51bWJlciBvZiByZWNvcmRzLCB0aGUgbWVkaWFuIGlzIHRoZSBtaWRkbGUgbnVtYmVyOyBpbiBjYXNlcyB3aXRoIGFuIGV2ZW4gbnVtYmVyIG9mIHJlY29yZHMsIHRoZSBtZWRpYW4gaXMgdGhlIGF2ZXJhZ2UgYmV0d2VlbiB0aGUgdHdvIG1pZGRsZSBudW1iZXJzIHRoYXQgZGl2aWRlIHRoZSBkYXRhIGludG8gbG93ZXIgYW5kIHVwcGVyIGhhbHZlcy4gIAoKIyMgQW5vbWFseSBEZXRlY3Rpb24KCkFsdGhvdWdoIHNvbWV0aW1lcywgb3V0bGllcnMgKGV4dHJlbWUgZGF0YSBwb2ludHMgZmFyIGZyb20gdGhlIG1lYW4pIGFyZSBtaXN0YWtlcyBtYWRlIGZyb20gZGF0YSBlbnRyeSwgdGhlcmUgd291bGQgYmUgc29tZXRoaW5nIGludGVyZXN0aW5nIGdvaW5nIG9uIHRvIHdhcnJhbnQgZnVydGhlciBpbnZlc3RpZ2F0aW9uLiAgSW4gZ2VuZXJhbCwgdGhvdWdoLCBlaXRoZXIgdGhlIG1lZGlhbiBvciB0cmltbWVkIG1lYW4gYXJlIHVzZWQsIHRyaW1taW5nIDEwJSBvZiB0aGUgbG93ZXN0IGFuZCBoaWdoZXN0IHZhbHVlcyBmcm9tIHRoZSBzb3J0ZWQgZGlzdHJpYnV0aW9uIGJlZm9yZSBjYWxjdWxhdGluZyB0aGUgbWVhbiwgaW4gdGhlIGxhdHRlciBjYXNlLiAgCgojIyBFeGFtcGxlOiBMb2NhdGlvbiBFc3RpbWF0ZXMgb2YgUG9wdWxhdGlvbiBhbmQgTXVyZGVyIFJhdGVzCgpJbiB0aGlzIGV4YW1wbGUsIHdlJ2xsIGV4cGxvcmUgdGhlIG11cmRlciByYXRlcyBhY3Jvc3MgdGhlIGNvdW50cnkgaW4gdGhlIGBzdGF0ZXMuY3N2YCBmaWxlLgoKYGBge3IgZXhwbG9yZS1zdGF0ZXN9CiMgTG9hZCBzdGF0ZXMgZmlsZSBhbmQgZXhwbG9yZSB0aGUgZGF0YQpzdGF0ZXMgPC0gcmVhZF9jc3YoIi4vZGF0YS9zdGF0ZS5jc3YiKQoKIyBMb29rIGF0IHRoZSBzdGF0ZXMgd2l0aCB0aGUgaGlnaGVzdCBtdXJkZXIgcmF0ZXMKc3RhdGVzICU+JSAKICBhcnJhbmdlKGRlc2MoTXVyZGVyLlJhdGUpKSAlPiUgCiAga2FibGUoZm9ybWF0ID0gImh0bWwiLCAKICAgICAgICBmb3JtYXQuYXJncyA9IGxpc3QoYmlnLm1hcmsgPSAiLCIpKSAlPiUgCiAgc2Nyb2xsX2JveChoZWlnaHQgPSAiNTAwcHgiLCAKICAgICAgICAgICAgIHdpZHRoID0gIjgwMHB4IikgJT4lIAogIGthYmxlX21hdGVyaWFsKGZ1bGxfd2lkdGggPSBUUlVFLCAic3RyaXBlZCIpCgojIFNjYWxlIHN0YXRlcyBkYXRhCnN0YXRlc19zY2FsZWQgPC0gc3RhdGVzICU+JSAKICBtdXRhdGUoUG9wdWxhdGlvbiA9IFBvcHVsYXRpb24gLyAxZTYpCmBgYAoKYGBge3IgZXhwbG9yZS1zdGF0ZXMtcGxvdH0KIyBFeHBsb3JhdG9yeSBwbG90ICh0cmFuc2Zvcm0gc2NhbGUgdG8gbWlsbGlvbnMpCnN0YXRlc19zY2FsZWQgJT4lIAogIGdncGxvdChhZXMoUG9wdWxhdGlvbiwgTXVyZGVyLlJhdGUpKSArIAogIGdlb21fcG9pbnQoKSArCiAgbXlfdGhlbWUgKwogIHNjYWxlX3hfY29udGludW91cyhuLmJyZWFrcyA9IDEwKSArCiAgbGFicyh4ID0gIlBvcHVsYXRpb24gKG1pbGxpb25zKSIsIAogICAgICAgeSA9ICJNdXJkZXIgcmF0ZSIsIAogICAgICAgdGl0bGUgPSAiTXVyZGVyIHJhdGUgZm9yIGVhY2ggVVMgc3RhdGUgZnJvbSAyMDEwIGNlbnN1cyBkYXRhIiwKICAgICAgIHN1YnRpdGxlID0gIkxvdWlzaWFuYSBoYWQgdGhlIGhpZ2hlc3QgbXVyZGVyIHJhdGUgKG51bWJlciBvZiBtdXJkZXJzIHBlciAxMDAsMDAwIHBlb3BsZSkiKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZmlsdGVyKHN0YXRlc19zY2FsZWQsIEFiYnJldmlhdGlvbiA9PSAiTEEiKSwgCiAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCAKICAgICAgICAgICAgIHNpemUgPSAyKSArCiAgZ2VvbV9sYWJlbChkYXRhID0gZmlsdGVyKHN0YXRlc19zY2FsZWQsIEFiYnJldmlhdGlvbiA9PSAiTEEiKSwKICAgICAgICAgICAgIGFlcyhsYWJlbCA9IFN0YXRlKSwgbnVkZ2VfeCA9IDQpCiAgCmBgYAoKV2UgY2FuIGNvbXB1dGUgdmFyaW91cyBzdW1tYXJ5IHN0YXRpc3RpY3Mgb24gdGhpcyBkYXRhc2V0IHRvIGdldCBhbiBvdmVydmlldyBvZiBtdXJkZXIgcmF0ZXMgYWNyb3NzIHRoZSBVbml0ZWQgU3RhdGVzLgoKYGBge3Igc3RhdGVzLW1lYW59CiMgQ29tcHV0ZSB2YXJpb3VzIHN1bW1hcnkgc3RhdHMgYW5kIHByaW50IHJlc3VsdHMKc3RhdGVzICU+JSAKICBzdW1tYXJpc2UobWVhbl9wb3BzaXplID0gbWVhbihQb3B1bGF0aW9uKSwgCiAgICAgICAgICAgIG1lYW5fdHJpbV9wb3BzaXplID0gbWVhbihQb3B1bGF0aW9uLCB0cmltID0gMC4xKSwgCiAgICAgICAgICAgIG1lZGlhbl9wb3BzaXplID0gbWVkaWFuKFBvcHVsYXRpb24pKSAlPiUgCiAga2FibGUoZm9ybWF0ID0gImh0bWwiLCAKICAgICAgICBmb3JtYXQuYXJncyA9IGxpc3QoYmlnLm1hcmsgPSAiLCIpLCAKICAgICAgICBjb2wubmFtZXMgPSBjKCJNZWFuIiwgIk1lYW4gKHRyaW1tZWQpIiwgIk1lZGlhbiIpKSAlPiUKICBhZGRfaGVhZGVyX2Fib3ZlKGMoIlBvcHVsYXRpb24gU2l6ZSIgPSAzKSkgJT4lIAogIGthYmxlX21hdGVyaWFsKGZ1bGxfd2lkdGggPSBGQUxTRSwgInN0cmlwZWQiKQoKYGBgCgpXZSBjYW4gYWxzbyBjYWxjdWxhdGUgdGhlIHdlaWdodGVkIG1lYW4gYW5kIG1lZGlhbiwgd2hpY2ggd2lsbCB0YWtlIGludG8gYWNjb3VudCB0aGUgcG9wdWxhdGlvbiBzaXplIGZvciBlYWNoIHN0YXRlLiAgVGhlIGtleSB0YWtlYXdheSBoZXJlIGlzIHRoYXQgYm90aCBlc3RpbWF0ZXMgYXJlIHJlbGF0aXZlbHkgc2ltaWxhci4gIAoKYGBge3Igc3RhdGVzLXdlaWdodGVkLXN0YXRpc3RpY3N9CiMgQ2FsY3VsYXRlIHdlaWdodGVkIHN0YXRpc3RpY3MgZm9yIGVhY2ggc3RhdGUgYmFzZWQgb24gdGhlaXIgcG9wdWxhdGlvbiBzaXplCnN0YXRlcyAlPiUgCiAgc3VtbWFyaXNlKHdlaWdodGVkX21lYW4gPSB3ZWlnaHRlZC5tZWFuKHggPSBzdGF0ZXMkTXVyZGVyLlJhdGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ID0gc3RhdGVzJFBvcHVsYXRpb24pLCAKICAgICAgICAgd2VpZ2h0ZWRfbWVkaWFuID0gd2VpZ2h0ZWRNZWRpYW4oeCA9IHN0YXRlcyRNdXJkZXIuUmF0ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHcgPSBzdGF0ZXMkUG9wdWxhdGlvbikpICU+JSAKICBrYWJsZShmb3JtYXQgPSAiaHRtbCIsIAogICAgICAgIGNvbC5uYW1lcyA9IGMoIk1lYW4iLCAiTWVkaWFuIikpICU+JSAKICBhZGRfaGVhZGVyX2Fib3ZlKGhlYWRlciA9IGMoIldlaWdodGVkIHN0YXRpc3RpY3MiID0gMikpICU+JSAKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGLCAic3RyaXBlZCIpICU+JSAKICBrYWJsZV9tYXRlcmlhbCgpCgpgYGAKCiMjIEVzdGltYXRlcyBvZiBWYXJpYWJpbGl0eQoKVmFyaWFiaWxpdHkgZGVzY3JpYmVzIHRoZSBzcHJlYWQgb2YgZGF0YSBhbmQgZGlzdGluZ3Vpc2hpbmcgaXQgZnJvbSByZWFsIHZhcmlhYmlsaXR5LiAgVGhlcmUgYXJlIGEgZmV3IHRlcm1zIHRvIHJldmlldywgaW5jbHVkaW5nIGRldmlhdGlvbnMgKGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIG9ic2VydmVkIGFuZCBlc3RpbWF0ZWQgdmFsdWVzLCBsaWtlIHRoZSBtZWFuLCBmb3IgZXhhbXBsZSksIHZhcmlhbmNlIChzdW0gb2Ygc3F1YXJlZCBkZXZpYXRpb25zIGZyb20gdGhlIG1lYW4sIGRpdmlkZWQgYnkgdGhlIHNhbXBsZSBzaXplIG1pbnVzIG9uZSksIGFuZCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uICh0aGUgc3F1YXJlIHJvb3Qgb2YgdGhlIHZhcmlhbmNlKS4gIFRoZXJlIGFyZSBtYW55IG90aGVyIG1ldHJpY3MgdXNlZCB0byBkZXNjcmliZSB2YXJpYWJpbGl0eSwgd2hpY2ggd2lsbCBiZSBkZW1vbnN0cmF0ZWQgdXNpbmcgZXhhbXBsZXMgYmVsb3cuCgpPbmUgbWV0cmljIHRvIGRlc2NyaWJlIHZhcmlhYmlsaXR5IGlzIHRoZSBtZWFuIGFic29sdXRlIGRldmlhdGlvbiwgb3IgdGhlIGF2ZXJhZ2Ugb2YgdGhlIGFic29sdXRlIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIG9ic2VydmVkIGFuZCBlc3RpbWF0ZWQgdmFsdWVzLiAgTW9yZSBjb21tb25seSB1c2VkIG1ldHJpY3MgYXJlIHRoZSB2YXJpYW5jZSAoYXZlcmFnZSBvZiB0aGUgc3F1YXJlZCBkZXZpYXRpb25zKSBhbmQgdGhlIHN0YW5kYXJkIGRldmlhdGlvbi4gIFRoZSBsYXR0ZXIgbWV0cmljIGlzIHByZWZlcnJlZCBvdmVyIHRoZSB2YXJpYW5jZSBiZWNhdXNlIGl0IGlzIGluIHRoZSBzYW1lIHNjYWxlIGFzIHRoZSBvcmlnaW5hbCBkYXRhLgoKYGBge3Igc3RhdGVzLXZhcmlhbmNlLXNkfQojIENhbGN1bGF0ZSBtZXRyaWNzIHRvIGRlc2NyaWJlIHZhcmlhYmlsaXR5CnN0YXRlcyAlPiUgCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKE11cmRlci5SYXRlKSwKICAgICAgICAgICAgdmFyaWFuY2UgPSB2YXIoTXVyZGVyLlJhdGUpLCAKICAgICAgICAgICAgc3RkX2RldiA9IHNkKE11cmRlci5SYXRlKSkKYGBgCgpPZnRlbiwgdGhlIHZhcmlhbmNlIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gZm9ybXVsYXMgdXNlIGBuLTFgIGluIHRoZWlyIGRlbm9taW5hdG9ycyB0byBnZW5lcmF0ZSB1bmJpYXNlZCBlc3RpbWF0ZXMuICBXaGVuIHVzaW5nIGp1c3QgYG5gLCB0aGUgc2FtcGxlIGVzdGltYXRlcyBvZiB0aGUgdmFyaWFuY2UgYW5kIHN0YW5kYXJkIGRldmlhdGlvbnMgd2lsbCBiZSBiaWFzZWQgYW5kIHVuZGVyZXN0aW1hdGUgdGhlIHRydWUgcG9wdWxhdGlvbiBwYXJhbWV0ZXJzLCBiZWNhdXNlIG9mIHRoZSBjb25zdHJhaW50IHRoYXQgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiByZXF1aXJlcyBjYWxjdWxhdGluZyB0aGUgc2FtcGxlIG1lYW4uCgpWYXJpYW5jZSBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIGFyZSBib3RoIHNlbnNpdGl2ZSB0byBvdXRsaWVycywgYnV0IGFub3RoZXIgbWV0cmljLCB0aGUgbWVkaWFuIGFic29sdXRlIGRldmlhdGlvbiBmcm9tIHRoZSBtZWRpYW4gKE1BRCksIGlzIG5vdCAoc2ltaWxhciB0byB0aGUgbWVkaWFuKS4gCgojIyBFc3RpbWF0ZXMgQmFzZWQgb24gUGVyY2VudGlsZXMKClRoZSBwZXJjZW50aWxlIGlzIHRoZSB2YWx1ZSBhdCB3aGljaCBhIGdpdmVuIHBlcmNlbnRhZ2Ugb2YgdGhlIGRhdGEgYXJlIGEgcGFydGljdWxhciB2YWx1ZSwgb3IgbGVzcy4gIEZvciBleGFtcGxlLCBpZiB3ZSB0b29rIGEgc2VxdWVuY2Ugb2YgbnVtYmVycyBmcm9tIDEgdG8gMSwwMDAgYW5kIHBpcGVkIHRoZW0gaW50byB0aGUgYHF1YW50aWxlKClgIGZ1bmN0aW9uLCB3ZSdkIHNlZSB0aGF0IHRoZSAyNXRoIHBlcmNlbnRpbGUgaXMgMjUwLjc1LiAgSW4gb3RoZXIgd29yZHMsIDI1JSBvZiB0aGUgdmFsdWVzIGZyb20gMSB0byAxLDAwMCBhcmUgMjUwLjc1IG9yIGxlc3MuICBUaGUgYElRUigpYCBmdW5jdGlvbiB3aWxsIHlpZWxkIHRoZSBpbnRlcnF1YXJ0aWxlIHJhbmdlLCBvciB0aGUgZXNzZW50aWFsbHkgd2hlcmUgdGhlIG1pZGRsZSA1MCUgb2YgdGhlIGRhdGEgbGllLiAgCgpCZWxvdywgd2UnbGwgZXhwbG9yZSB0aGUgYHN0YXRlc2AgZGF0YXNldCB1c2luZyB0aGUgZXN0aW1hdGVzIG9mIHZhcmlhYmlsaXR5LiAgCgpgYGB7ciBzdGF0ZXMtdmFyaWFiaWxpdHktcGxvdH0Kc3RhdGVzICU+JSAKICBtdXRhdGUoUG9wdWxhdGlvbiA9IFBvcHVsYXRpb24gLyAxZTYpICU+JSAKICBzdW1tYXJpc2UoU0QgPSBzZChQb3B1bGF0aW9uKSwKICAgICAgICAgICAgSVFSID0gSVFSKFBvcHVsYXRpb24pLCAKICAgICAgICAgICAgTUFEID0gbWFkKFBvcHVsYXRpb24pKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBldmVyeXRoaW5nKCksCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIm1ldHJpYyIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJlc3RpbWF0ZSIpICU+JSAKICBnZ3Bsb3QoYWVzKG1ldHJpYywgZXN0aW1hdGUpKSArCiAgZ2VvbV9wb2ludCgpICsKICBteV90aGVtZSArCiAgbGFicyh4ID0gIlN0YXRpc3RpYyIsIAogICAgICAgeSA9ICJQb3B1bGF0aW9uIChtaWxsaW9ucykiKQogIApgYGAKCkZyb20gdGhpcyBwbG90LCBpdCdzIG9idmlvdXMgdGhhdCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIChTRCkgaXMgYWJvdXQgMS43IHRpbWVzIGdyZWF0ZXIgdGhhbiB0aGUgTUFELCBiZWNhdXNlIGl0IGlzIHNlbnNpdGl2ZSB0byBvdXRsaWVycy4gIAoKIyMgRXhwbG9yaW5nIHRoZSBEYXRhIERpc3RyaWJ1dGlvbgoKVGhlcmUgYXJlIHNldmVyYWwgYWRkaXRpb25hbCBhcHByb2FjaGVzIHRvIHZpc3VhbGl6aW5nIGRpc3RyaWJ1dGlvbnMsIGluY2x1ZGluZyBoaXN0b2dyYW1zIGFuZCBkZW5zaXR5IHBsb3RzLiAgVGhlc2UgdHdvIHZpc3VhbGl6YXRpb25zIGFyZSBzaW1pbGFyLCBleGNlcHQgdGhlIGhpc3RvZ3JhbXMgYXJlIGJhc2VkIG9uIGJpbm5pbmcgb2JzZXJ2YXRpb25zIGludG8gZGlzY3JldGUgYmlucywgd2hpbGUgdGhlIGxhdHRlciBpbnZvbHZlcyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0aW9uIChLREUpIHRvIGFwcHJveGltYXRlIHRoZSBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24uICBLREUgaXMgYSBub24tcGFyYW1ldHJpYyBhcHByb2FjaCB0byBhcHByb3hpbWF0aW5nIHRoZSBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24gYmVjYXVzZSBzb21ldGltZXMgZGF0YSBkb24ndCBjb25mb3JtIHRvIHRoZSBzdGFuZGFyZCBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb25zIChlLmcuLCBpZiBkYXRhIGFyZSBiaW1vZGFsLCBvciBtdWx0aW1vZGFsKS4gIEluIHBhcmFtZXRyaWMgYXBwcm9hY2hlcywgd2UgY2FuIGRlc2NyaWJlIHRoZSBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24gdXNpbmcgcGFyYW1ldGVycy4gIEZvciBleGFtcGxlLCBpZiBkYXRhIGFyZSBub3JtYWxseSBkaXN0cmlidXRlZCwgd2UgZGVzY3JpYmUgdGhlIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiB1c2luZyB0d28gcGFyYW1ldGVyczogdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbi4gIAoKVG8gZGVzY3JpYmUgdGhlIG1pZGRsZSA1MCUgb2YgdGhlIGRhdGEgZm9yIHZhcmlvdXMgZmllbGRzLCBvciB0aGUgcmFuZ2UgYmV0d2VlbiB0aGUgMjV0aCBhbmQgNzV0aCBwZXJjZW50aWxlLCB3ZSBjYW4gdXNlIGEgYm94cGxvdC4gIFRoZSBkYXRhIHBvaW50cyBvdXRzaWRlIG9mIDEuNSB0aW1lcyB0aGUgSVFSIGFyZSBkZWZpbmVkIGFzIG91dGxpZXJzIChhbHNvIHNob3duKS4gIE5vdGUgaW4gdGhlIGJveHBsb3RzLCB0aGUgZnVuY3Rpb24gY2FsbCBzaG91bGQgYmUgYGdncGxvdChhZXMoIiIsIFBvcHVsYXRpb24pKWAsIG9yIGVsc2UgdGhlcmUgd2lsbCBiZSBlcnJvbmVvdXMgZGF0YSBvbiB0aGUgeC1heGlzLiAgCgpgYGB7ciBzdGF0ZXMtYm94cGxvdHN9CiMgUG9wdWxhdGlvbgpwb3B1bGF0aW9uX3Bsb3QgPC0gc3RhdGVzX3NjYWxlZCAlPiUgCiAgbXV0YXRlKE91dGxpZXIgPSBjYXNlX3doZW4oCiAgICBQb3B1bGF0aW9uID4gcXVhbnRpbGUoUG9wdWxhdGlvbiwgMC43NSkgKyAxLjUgKiBJUVIoUG9wdWxhdGlvbikgfiAiT3V0bGllciIsCiAgICBUUlVFIH4gIk5vbm91dGxpZXIiKQogICkgJT4lIAogIGdncGxvdChhZXMoIiIsIFBvcHVsYXRpb24pKSArCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3IgPSAicmVkIikgKwogIG15X3RoZW1lICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHkgPSAiUG9wdWxhdGlvbiAobWlsbGlvbnMpIiwgCiAgICAgICB0aXRsZSA9ICJUaGUgbWVkaWFuIHBvcHVsYXRpb24gc2l6ZSBpbiB0aGU8YnI+VW5pdGVkIFN0YXRlcyBpcyA0LjQgbWlsbGlvbiBwZW9wbGUiLCAKICAgICAgIHN1YnRpdGxlID0gIlRoZXJlIGFyZSBmb3VyIG91dGxpZXJzIGJ5IHBvcHVsYXRpb24gc2l6ZTo8YnI+Q2FsaWZvcm5pYSAoMzcuMyksIFRleGFzICgyNS4xKSw8YnI+TmV3IFlvcmsgKDE5LjQpLCBhbmQgRmxvcmlkYSAoMTguOCkiKSAKCiMgTXVyZGVyIHJhdGUKbXVyZGVyX3JhdGVfcGxvdCA8LSBzdGF0ZXNfc2NhbGVkICU+JSAKICBtdXRhdGUoT3V0bGllciA9IGNhc2Vfd2hlbigKICAgIE11cmRlci5SYXRlID4gcXVhbnRpbGUoTXVyZGVyLlJhdGUsIDAuNzUpICsgMS41ICogSVFSKE11cmRlci5SYXRlKSB+ICJPdXRsaWVyIiwKICAgIFRSVUUgfiAiTm9ub3V0bGllciIpCiAgKSAlPiUgCiAgZ2dwbG90KGFlcygiIiwgTXVyZGVyLlJhdGUpKSArCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3IgPSAicmVkIikgKwogIG15X3RoZW1lICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHkgPSAiTXVyZGVyIHJhdGUiLCAKICAgICAgIHRpdGxlID0gIlRoZSBtZWRpYW4gbXVyZGVyIHJhdGU8YnI+KCMgbXVyZGVycy8xMDBLKSBpbiB0aGU8YnI+VW5pdGVkIFN0YXRlcyBpcyA0LjAiLAogICAgICAgc3VidGl0bGUgPSAiTG91aXNpYW5hIGlzIHRoZSBvbmx5IG91dGxpZXIgd2l0aDxicj5hIG11cmRlciByYXRlIG9mIDEwLjAiKQoKIyBQdXQgdGhlbSB0b2dldGhlcgpwb3B1bGF0aW9uX3Bsb3QgKwogIG11cmRlcl9yYXRlX3Bsb3QgKwogIHBsb3RfYW5ub3RhdGlvbih0YWdfbGV2ZWxzID0gIkEiLCAKICAgICAgICAgICAgICAgICAgdGFnX3N1ZmZpeCA9ICIpIikKYGBgCgojIyBTdGF0aXN0aWNhbCBNb21lbnRzCgpUaGVyZSBhcmUgZm91ciBtb21lbnRzIG9mIGEgZGlzdHJpYnV0aW9uOgoKMSkgTG9jYXRpb24gKHJlZmVycmluZyB0byBtZWFuLCBtZWRpYW4sIG1vZGUpCgoyKSBWYXJpYWJpbGl0eSAoZS5nLiwgc3RhbmRhcmQgZGV2aWF0aW9uLCB2YXJpYW5jZSwgTUFEKQoKMykgU2tld25lc3MgKGFyZSBkYXRhIHNrZXdlZCB0byBsYXJnZSBvciBzbWFsbCB2YWx1ZXMpCgo0KSBLdXJ0b3NpcyAoZGVzY3JpYmVzIHRoZSB0ZW5kZW5jeSBmb3IgZGF0YSB0byBoYXZlIGV4dHJlbWUgdmFsdWVzKQoKIyMgRXhwbG9yaW5nIEJpbmFyeSBhbmQgQ2F0ZWdvcmljYWwgRGF0YQoKSW4gdGhlIG5leHQgc2VjdGlvbiwgd2UnbGwgZXhwbG9yZSBjYXRlZ29yaWNhbCBkYXRhIHVzaW5nIHRoZSBgZGZ3X2FpcmxpbmUuY3N2YCBkYXRhc2V0LCB3aGljaCBjb250YWlucyBkYXRhIG9uIHRoZSBudW1iZXIgb2YgZGVsYXllZCBmbGlnaHRzIHBlciB5ZWFyIGZyb20gRGFsbGFzL0ZvcnQgV29ydGggYWlycG9ydCBkdWUgdG8gdmFyaW91cyByZWFzb25zOiBjYXJyaWVyIGlzc3VlcywgYWlyIHRyYWZmaWMgY29udHJvbCBwcm9ibGVtcywgd2VhdGhlciwgc2VjdXJpdHksIG9yIGlzc3VlcyB3aXRoIGluYm91bmQgZmxpZ2h0cy4KCmBgYHtyIGdsaW1wc2UtZGZ3LWRhdGF9CiMgSW1wb3J0IGRhdGEKZGZ3X2FpcmxpbmUgPC0gcmVhZF9jc3YoIi4vZGF0YS9kZndfYWlybGluZS5jc3YiKQoKIyBWaWV3IGRhdGEKZGZ3X2FpcmxpbmUgJT4lIAogIGtibCgpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFKSAlPiUgCiAga2FibGVfY2xhc3NpY18yKCkKICAKYGBgCgpUaGVzZSBkYXRhIGNhbiBhbHNvIGJlIHJlcHJlc2VudGVkIHVzaW5nIGEgYmFyIHBsb3QuCgpgYGB7ciBkZnctYmFycGxvdH0KIyBNb2RpZnkgZGZ3IGFpcmxpbmUgZGF0YSB3aXRoIHBpdm90IGxvbmdlcgpkZndfYWlybGluZV9sb25nZXIgPC0gZGZ3X2FpcmxpbmUgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gZXZlcnl0aGluZygpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJDYXVzZV9vZl9kZWxheSIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJOdW1fZGVsYXlzIikgCiAgCiMgUGxvdApkZndfYWlybGluZV9sb25nZXIgJT4lIAogIGdncGxvdChhZXMoQ2F1c2Vfb2ZfZGVsYXksIE51bV9kZWxheXMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIAogICAgICAgICAgIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBOdW1fZGVsYXlzKSwgCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpLCAKICAgICAgICAgICAgdmp1c3QgPSAtMC4yNSkgKwogIG15X3RoZW1lICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsIDAuMSkpKSArCiAgbGFicyh4ID0gIkNhdXNlIG9mIERlbGF5IiwgCiAgICAgICB5ID0gIk51bWJlciBvZiBkZWxheXMgcGVyIHllYXIiLCAKICAgICAgIHRpdGxlID0gIk1vc3QgZGVsYXlzIGZyb20gRGFsbGFzL0ZvcnQgV29ydGggYWlycG9ydCBhcmUgZHVlIHRvIGluY29taW5nIGZsaWdodCBkZWxheXMiKQoKYGBgCgojIyBDb3JyZWxhdGlvbgoKVGhlIGNvcnJlbGF0aW9uIGRlc2NyaWJlcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdHdvIHZhcmlhYmxlcyBvbiBhIGNvbnRpbnVvdXMgLTEgdG8gMSBzY2FsZS4gIEl0IGlzIGNhbGN1bGF0ZWQgYnkgdGFraW5nIHRoZSBkZXZpYXRpb25zIGZyb20gdGhlIG1lYW4gZm9yIG9uZSB2YXJpYWJsZSwgbXVsdGlwbGllZCBieSB0aGUgZGV2aWF0aW9ucyBmcm9tIHRoZSBtZWFuIGZvciB0aGUgc2Vjb25kIHZhcmlhYmxlLCBhbmQgdGhlbiBkaXZpZGluZyBieSB0aGUgcHJvZHVjdCBvZiB0aGUgc3RhbmRhcmQgZGV2aWF0aW9ucy4gIEluIHRoaXMgc2VjdGlvbiwgd2UnbGwgZXhwbG9yZSB0aGUgYHNwNTAwX3B4LmNzdmAgZGF0YXNldCwgd2hpY2ggY29udGFpbnMgNSw2NDcgcmVjb3JkcyBvZiB0ZWxlY29tbXVuaWNhdGlvbiBzdG9ja3MgZnJvbSA1MTcgZGlmZmVyZW50IGNvbXBhbmllcyBvdmVyIHRpbWUuICBUbyBjcmVhdGUgdGhlIHBsb3QgaW4gdGhlIHRleHRib29rLCB3ZSdsbCBhbHNvIGltcG9ydCB0aGUgYHNwNTAwX3N5bS5jc3ZgIGRhdGFzZXQsIHdoaWNoIGNvbnRhaW5zIGRhdGEgb24gdGhlIGNvbXBhbnkgc3ltYm9scyBhbmQgc2VjdG9ycy4gIFdlJ2xsIGpvaW4gYm90aCBvZiB0aGVzZSB0b2dldGhlciwgdGhlbiBmaWx0ZXIgdGhlIGZpbmFsIGRhdGFzZXQgdG8gb25seSBpbmNsdWRlIHRob3NlIHJlY29yZHMgZm9yIG1ham9yIGV4Y2hhbmdlLXRyYW5kZWQgZnVuZHMgKEVURnMpIGJldHdlZW4gSnVseSAyMDEyIHRocm91Z2ggSnVuZSAyMDE1LiAKCmBgYHtyIHRlbGVjb21tdW5pY2F0aW9ucy1kYXRhLWNsZWFudXB9CiMgSW1wb3J0IHJldHVybnMgZGF0YQp0ZWxlY29tX3JldHVybnMgPC0gcmVhZF9jc3YoIi4vZGF0YS9zcDUwMF9weC5jc3YiKSAlPiUgCiAgcmVuYW1lKCJEYXRlIiA9IFgxKQoKIyBJbXBvcnQgbWV0YWRhdGEKdGVsZWNvbV9tZXRhZGF0YSA8LSByZWFkX2NzdigiLi9kYXRhL3NwNTAwX3N5bS5jc3YiKQoKIyBKb2luCnRlbGVjb21fcmV0dXJucyAlPiUgCiAgZmlsdGVyKERhdGUgPiAiMjAxMi0wNy0wMSIgJiBEYXRlIDwgIjIwMTUtMDYtMDEiKSAKICBwaXZvdF9sb25nZXIoY29scyA9IGV2ZXJ5dGhpbmcoKSwgCiAgICAgICAgIG5hbWVzX3RvID0gInN5bWJvbCIsIAogICAgICAgICB2YWx1ZXNfdG8gPSAiZGFpbHlfcmV0dXJucyIpICU+JSAKICBsZWZ0X2pvaW4odGVsZWNvbV9tZXRhZGF0YSwgYnkgPSAic3ltYm9sIikgJT4lIAogIGZpbHRlcihzZWN0b3IgPT0gImV0ZiIpIAoKYGBgCgpOb3csIHdlJ2xsIGNhbGN1bGF0ZSB0aGUgY29ycmVsYXRpb24gb2YgZGFpbHkgcmV0dXJucyBiZXR3ZWVuIGFsbCBjb21wYW5pZXMuCgpgYGB7ciBldGYtY29ycmVsYXRpb24tcGxvdH0KZXRmX2RhdGEgJT4lIAogIHNlbGVjdChzeW1ib2wsIGRhaWx5X3JldHVybnMpICU+JSAKICBtdXRhdGUocm93bnVtYmVyID0gcm93X251bWJlcigpKSAlPiUgCiAgcGl2b3Rfd2lkZXIoaWRfY29scyA9IHJvd251bWJlciwgCiAgICAgICAgICAgICAgbmFtZXNfZnJvbSA9ICJzeW1ib2wiLCAKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9ICJkYWlseV9yZXR1cm5zIikKCmBgYAoKCg==